﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="NoiseMapBuilderPlane.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
//
//    Based on libnoise by Jason Bevins
//      Copyright (C) 2003, 2004 Jason Bevins (licensed under LGPL)
// </copyright>
// <license>
//   This file is part of starLiGHT.Noise.
//
//   starLiGHT.Noise is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Noise is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Noise. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Noise are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 10385                 $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-06-14 07:59:17 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Noise.Noise
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;

    #endregion

    /// Builds a planar noise map.
    ///
    /// This class builds a noise map by filling it with coherent-noise values
    /// generated from the surface of a plane.
    ///
    /// This class describes these input values using (x, z) coordinates.
    /// Their y coordinates are always 0.0.
    ///
    /// The application must provide the lower and upper x coordinate bounds
    /// of the noise map, in units, and the lower and upper z coordinate
    /// bounds of the noise map, in units.
    ///
    /// To make a tileable noise map with no seams at the edges, call the
    /// EnableSeamless() method.
    public class NoiseMapBuilderPlane : NoiseMapBuilder
    {
        /// A flag specifying whether seamless tiling is enabled.
        private bool isSeamlessEnabled;

        /// Lower x boundary of the planar noise map, in units.
        private double lowerXBound;

        /// Lower z boundary of the planar noise map, in units.
        private double lowerZBound;

        /// Upper x boundary of the planar noise map, in units.
        private double upperXBound;

        /// Upper z boundary of the planar noise map, in units.
        private double upperZBound;

        /// Constructor.
        public NoiseMapBuilderPlane()
        {
        }

        public override void Build()
        {
            if (this.upperXBound <= this.lowerXBound
                || this.upperZBound <= this.lowerZBound
                || this.destWidth <= 0
                || this.destHeight <= 0
                || this.sourceModule == null
                || this.destNoiseMap == null)
            {
                throw new Exception("invalid param");
            }

            // Resize the destination noise map so that it can store the new output
            // values from the source model.
            this.destNoiseMap.SetSize(this.destWidth, this.destHeight);

            // Create the plane model.
            Plane planeModel = new Plane();
            planeModel.SetModule(this.sourceModule);

            double extentX = this.upperXBound - this.lowerXBound;
            double extentZ = this.upperZBound - this.lowerZBound;
            double deltaX = extentX / (double)this.destWidth;
            double deltaZ = extentZ / (double)this.destHeight;
            double curX = this.lowerXBound;
            double curZ = this.lowerZBound;

            // Fill every point in the noise map with the output values from the model.
            for (int z = 0; z < this.destHeight; z++)
            {
                float dest = this.destNoiseMap.GetSlabPtr(z);
                curX = this.lowerXBound;
                for (int x = 0; x < this.destWidth; x++)
                {
                    float finalValue;
                    if (!this.isSeamlessEnabled)
                    {
                        finalValue = (float)planeModel.GetValue(curX, curZ, this.lowerXBound, this.upperXBound, this.lowerZBound, this.upperZBound);
                    }
                    else
                    {
                        double valueSW = planeModel.GetValue(curX, curZ, this.lowerXBound, this.upperXBound, this.lowerZBound, this.upperZBound);
                        double valueSE = planeModel.GetValue(curX + extentX, curZ, this.lowerXBound, this.upperXBound, this.lowerZBound, this.upperZBound);
                        double valueNW = planeModel.GetValue(curX, curZ + extentZ, this.lowerXBound, this.upperXBound, this.lowerZBound, this.upperZBound);
                        double valueNE = planeModel.GetValue(curX + extentX, curZ + extentZ, this.lowerXBound, this.upperXBound, this.lowerZBound, this.upperZBound);
                        double blendX = 1.0 - ((curX - this.lowerXBound) / extentX);
                        double blendZ = 1.0 - ((curZ - this.lowerZBound) / extentZ);
                        double z0 = interp.LinearInterp(valueSW, valueSE, blendX);
                        double z1 = interp.LinearInterp(valueNW, valueNE, blendX);
                        finalValue = (float)interp.LinearInterp(z0, z1, blendZ);
                    }
                    ////////TODO: *pDest++ = finalValue;
                    this.destNoiseMap.SetValue(x, z, finalValue);
                    curX += deltaX;
                }
                curZ += deltaZ;
                ////if (this.pCallback != null)
                ////{
                ////    this.pCallback (z);
                ////}
            }
        }

        /// Enables or disables seamless tiling.
        ///
        /// @param enable A flag that enables or disables seamless tiling.
        ///
        /// Enabling seamless tiling builds a noise map with no seams at the
        /// edges.  This allows the noise map to be tileable.
        public void EnableSeamless(bool enable)
        {
            this.isSeamlessEnabled = enable;
        }

        public void EnableSeamless()
        {
            this.isSeamlessEnabled = true;
        }

        /// Returns the lower x boundary of the planar noise map.
        ///
        /// @returns The lower x boundary of the planar noise map, in units.
        public double GetLowerXBound()
        {
            return this.lowerXBound;
        }

        /// Returns the lower z boundary of the planar noise map.
        ///
        /// @returns The lower z boundary of the noise map, in units.
        public double GetLowerZBound()
        {
            return this.lowerZBound;
        }

        /// Returns the upper x boundary of the planar noise map.
        ///
        /// @returns The upper x boundary of the noise map, in units.
        public double GetUpperXBound()
        {
            return this.upperXBound;
        }

        /// Returns the upper z boundary of the planar noise map.
        ///
        /// @returns The upper z boundary of the noise map, in units.
        public double GetUpperZBound()
        {
            return this.upperZBound;
        }

        /// Determines if seamless tiling is enabled.
        ///
        /// @returns
        /// - @a true if seamless tiling is enabled.
        /// - @a false if seamless tiling is disabled.
        ///
        /// Enabling seamless tiling builds a noise map with no seams at the
        /// edges.  This allows the noise map to be tileable.
        public bool IsSeamlessEnabled()
        {
            return this.isSeamlessEnabled;
        }

        /// Sets the boundaries of the planar noise map.
        ///
        /// @param lowerXBound The lower x boundary of the noise map, in
        /// units.
        /// @param upperXBound The upper x boundary of the noise map, in
        /// units.
        /// @param lowerZBound The lower z boundary of the noise map, in
        /// units.
        /// @param upperZBound The upper z boundary of the noise map, in
        /// units.
        ///
        /// @pre The lower x boundary is less than the upper x boundary.
        /// @pre The lower z boundary is less than the upper z boundary.
        ///
        /// @throw noise::ExceptionInvalidParam See the preconditions.
        public void SetBounds(double lowerXBound, double upperXBound, double lowerZBound, double upperZBound)
        {
            if (lowerXBound >= upperXBound
              || lowerZBound >= upperZBound)
            {
                throw new Exception("invalid param");
            }

            this.lowerXBound = lowerXBound;
            this.upperXBound = upperXBound;
            this.lowerZBound = lowerZBound;
            this.upperZBound = upperZBound;
        }
    }
}

